﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.Runtime.Serialization;
using haptic.src.engine.debug;
using haptic.src.engine.render;
using haptic.src.engine.collisions;

namespace haptic.src.engine
{
    [Serializable]
    [XmlRootAttribute("level")]
    public class Level : Loadable, ISerializable
    {
        #region Constructors
        public Level(){}
        public Level(Scene oScene, IServiceProvider oServiceProvider, string sFilePath)
        {
            Init(oScene, oServiceProvider, sFilePath);
        }
        public Level(SerializationInfo oInfo, StreamingContext oContext)
        {
            m_oEntities = (List<Entity>)oInfo.GetValue("entities", typeof(List<Entity>));
        }
        #endregion

        public void Init(Scene oGame, IServiceProvider oServiceProvider, string sFilePath)
        {
            m_oScene = oGame;
            m_oContent = new ContentManager(oServiceProvider, "Content");
            m_sFilePath = sFilePath;
        }
        public void GetObjectData(SerializationInfo oInfo, StreamingContext oContext)
        {
            oInfo.AddValue("entities", m_oEntities);
        }
        #region Load
        public override bool Load()
        {
            m_bFirstUpdate = true;

            // Init
            foreach (Entity oEntity in m_oEntities)
            {
                oEntity.Init(m_oScene);
            }

            // Preload
            foreach (Entity oEntity in m_oEntities)
            {
                oEntity.PreLoad(m_oScene);
            }

            // Load
            foreach (Entity oEntity in m_oEntities)
            {
                oEntity.Load(m_oScene, m_oContent);
            }

            // Postload
            foreach (Entity oEntity in m_oEntities)
            {
                oEntity.PostLoad(m_oScene);
            }
            return true;
        }
        public override void Unload()
        {
            // Unload
            foreach (Entity oEntity in m_oEntities)
            {
                oEntity.Unload(m_oScene);
            }
        }
        private void Start()
        {
            foreach (Entity oEntity in m_oEntities)
            {
                oEntity.Start();
            }
        }
        #endregion

        #region Edit
        public void Add(Entity oEntity)
        {
            m_oEntities.Add(oEntity);
            AddToLayer(oEntity);
        }
        private Layer AddToLayer(Entity oEntity)
        {
            Layer oLayer = GetLayer(oEntity.m_iLayer);
            if (oLayer == null)
            {
                oLayer = new Layer();
                oLayer.m_iId = oEntity.m_iLayer;
                m_oLayers.Add(oEntity.m_iLayer, oLayer);
            }
            oLayer.AddEntity(oEntity);
            return oLayer;
        }
        public Layer GetLayer(float fDepth)
        {
            foreach (KeyValuePair<int,Layer> oPair in m_oLayers)
            {
                if (oPair.Value.m_fDepth == fDepth)
                    return oPair.Value;
            }
            return null;
        }
        public Layer GetLayer(int iId)
        {
            foreach (KeyValuePair<int, Layer> oPair in m_oLayers)
            {
                if (oPair.Key == iId)
                    return oPair.Value;
            }
            return null;
        }
        #endregion

        #region Update
        public void Update(GameTime oTime)
        {
            if (m_bFirstUpdate)
            {
                foreach (Entity oEntity in m_oEntities)
                    oEntity.Start();
            }
            m_bFirstUpdate = false;
            foreach(Entity oEntity in m_oEntities)
                oEntity.Update(oTime);
            foreach(Entity oEntity in m_oEntities)
                oEntity.PostUpdate(oTime);
        }
        #endregion

        #region Draw
        public void Draw(Renderer oRenderer, GameTime oTime)
        {
            foreach (KeyValuePair<int, Layer> oPair in m_oLayers)
            {
                oRenderer.BeginRender((float)(oPair.Key) * F_REAL_LAYER_DEPTH_CONVERTER);
                oPair.Value.Draw(oRenderer, oTime);
                oRenderer.EndRender();
            }
        }
        public void DrawDebug(DebugRenderer oDebug, GameTime oTime)
        {
            foreach (KeyValuePair<int, Layer> oPair in m_oLayers)
            {
                oPair.Value.DrawDebug(oDebug, oTime);
            }
        }
        #endregion

        #region Constants
        private const int I_PLAYER_LAYER = 1000;
        private const float F_REAL_LAYER_DEPTH_CONVERTER = 0.001f;
        #endregion

        #region Attributes
        private class LayerComparer : IComparer<int>
        {
            public int Compare(int a, int b)
            {
                return b - a;
            }
        }
        private ContentManager  m_oContent;
        private List<Entity> m_oEntities = new List<Entity>();
        public List<Entity> entities { get { return m_oEntities; } }
        private SortedList<int, Layer> m_oLayers = new SortedList<int, Layer>(new LayerComparer());
        [System.Xml.Serialization.XmlIgnoreAttribute]
        public string m_sFilePath;
        private Scene m_oScene;
        private bool m_bFirstUpdate = true;
        #endregion
    }
}
